package com.zyl.blog.common.utils.Extreme;

import com.zyl.blog.common.exception.BlogException;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 雪花算法ID生成工具类
 * 生成64位的分布式唯一ID
 * 结构：1位符号位 + 41位时间戳 + 10位机器ID + 12位序列号
 * @version v1.0
 * @ProjectName: Personal_Blog
 * @Package: com.zyl.common.utils.Extreme
 * @ClassName: IdUtil
 * @Author FinalFantasy
 * @Date 2025/2/17-21:11
 */
public class SnowIdUtil {
    /**
     * 起始时间戳 (2023-01-01 00:00:00)
     */
    private static final long START_TIMESTAMP = 1672502400000L;

    /**
     * 机器ID所占的位数
     */
    private static final long WORKER_ID_BITS = 10L;

    /**
     * 序列号所占的位数
     */
    private static final long SEQUENCE_BITS = 12L;

    /**
     * 机器ID的最大值
     */
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);

    /**
     * 序列号的最大值
     */
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * 机器ID向左移12位
     */
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间戳向左移22位(10+12)
     */
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;

    /**
     * 工作机器ID
     */
    private final long workerId;

    /**
     * 序列号
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 锁
     */
    private final Lock lock = new ReentrantLock();

    /**
     * 单例缓存
     */
    private static final ConcurrentMap<Long, SnowIdUtil> INSTANCES = new ConcurrentHashMap<>();

    /**
     * 私有构造函数
     */
    private SnowIdUtil(long workerId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
        }
        this.workerId = workerId;
    }

    /**
     * 获取单例实例
     * 默认使用0号机器ID，集群环境下应根据实际机器配置
     */
    public static SnowIdUtil getInstance() {
        return getInstance(0L);
    }

    /**
     * 获取指定机器ID的单例实例
     */
    public static SnowIdUtil getInstance(long workerId) {
        return INSTANCES.computeIfAbsent(workerId, SnowIdUtil::new);
    }

    /**
     * 生成下一个ID
     */
    public synchronized long nextId() {
        lock.lock();
        try {
            long timestamp = System.currentTimeMillis();

            // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟回退过，抛出异常
            if (timestamp < lastTimestamp) {
                throw new BlogException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }

            // 如果是同一时间生成的，则进行序列号自增
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & MAX_SEQUENCE;
                // 序列号达到最大值，阻塞到下一个毫秒
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                // 时间戳改变，序列号重置
                sequence = 0L;
            }

            // 更新上次生成ID的时间戳
            lastTimestamp = timestamp;

            // 移位并通过或运算拼到一起组成64位的ID
            return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                    | (workerId << WORKER_ID_SHIFT)
                    | sequence;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 阻塞到下一个毫秒，直到获得新的时间戳
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    /**
     * 生成雪花算法ID（静态方法便捷调用）
     */
    public static long getSnowflakeNextId() {
        return getInstance().nextId();
    }

    /**
     * 生成雪花算法ID字符串（静态方法便捷调用）
     */
    public static String getSnowflakeNextIdStr() {
        return String.valueOf(getInstance().nextId());
    }
}
